package net.atomarrow.ioc;

import net.atomarrow.action.Action;
import net.atomarrow.action.AtomarrowRequest;
import net.atomarrow.annotation.Inject;
import net.atomarrow.db.pool.ConnectionProvider;
import net.atomarrow.render.Render;
import net.atomarrow.upload.MultipartRequest;
import net.atomarrow.util.StringUtil;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Stack;

public class ActionContext {
    /**
     * url param ,use getAttribute setAttribute
     */
    public static final String URLPARAM = "URLPARAM";
    @Inject
    private ConnectionProvider connectionProvider;
    private static final ThreadLocal<ActionContext> contextThreadLocal = new ThreadLocal<ActionContext>();
    private HttpSession session;
    private Action action;
    private HttpServletRequest request;
    private ServletContext application;
    private MultipartRequest multipartRequest;
    private HttpServletResponse response;
    private Connection connection;
    private Render render;
    private boolean needCheckTransaction = true;
    private boolean needTx;
    private Stack<Method> methodStack;
    private boolean needRollback;
    private String nextAction;
    private String[] urlParam;
    private Throwable controllerOrInterceptorException;
    private Throwable serviceException;
    private HashMap dataMap;

    private ActionContext(Action action, HttpServletRequest request, MultipartRequest multipartRequest,
                          HttpServletResponse response) {
        this.action = action;
        this.request = request == null ? null : new AtomarrowRequest(request, action);
        if (request != null) {
            this.session = request.getSession();
            this.application = request.getServletContext();
        }
        this.multipartRequest = multipartRequest;
        this.response = response;
        methodStack = new Stack<Method>();
        processUrlParam(request);
    }


    private void processUrlParam(HttpServletRequest request) {
        if (request == null) {
            return;
        }
        String param = (String) request.getAttribute(URLPARAM);
        if (param != null) {
            if (param.indexOf("-") == -1) {
                urlParam = new String[]{param};
            } else if (StringUtil.count(param, '-') <= 30) {
                urlParam = param.split("-");
                for (int i = 0; i < urlParam.length; i++) {
                    if ("".equals(urlParam[i])) {
                        urlParam[i] = null;
                    }
                }
            }
        }
    }


    public static ActionContext getContext() {
        return contextThreadLocal.get();
    }

    public static void init(Action action, HttpServletRequest request, MultipartRequest multipartRequest,
                            HttpServletResponse response) {
        ActionContext actionContext = new ActionContext(action, request, multipartRequest, response);
        ApplicationContext.inject(actionContext);
        contextThreadLocal.set(actionContext);
    }

    public void setApplication(ServletContext application) {
        getContext().application = application;
    }

    public static void destory() {
        getContext().closeConnection();
        getContext().contextThreadLocal.remove();
    }

    public HttpSession getSession() {
        return session;
    }

    public Connection getConnection() {
        if (connection == null) {
            connection = connectionProvider.getConection(needTx);
        }
        return connection;
    }

    public void closeConnection() {
        Connection temp = connection;
        connection = null;
        connectionProvider.closeConnection(temp);
    }

    public Action getAction() {
        return action;
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public HttpServletResponse getResponse() {
        return response;
    }

    public Render getRender() {
        return render;
    }

    public void setRender(Render render) {
        this.render = render;
    }

    public boolean isNeedCheckTransaction() {
        return needCheckTransaction;
    }

    public void setNeedCheckTransaction(boolean needCheckTransaction) {
        this.needCheckTransaction = needCheckTransaction;
    }

    public boolean isNeedTx() {
        return needTx;
    }

    public void setNeedTx(boolean needTx) {
        if (this.needTx != needTx) {
            connectionProvider.changeAutoCommitState(connection, needTx);
        }
        this.needTx = needTx;
    }

    public void commitTx() {
        connectionProvider.commitTx(connection);
        setNeedTx(false);
    }

    public void rollback() {
        connectionProvider.rollbackTx(connection);
        setNeedRollback(false);
        setNeedTx(false);
    }

    public void push(Method method) {
        methodStack.push(method);
    }

    public void pop() {
        methodStack.pop();
        if (methodStack.isEmpty()) {
            setNeedCheckTransaction(true);
        }
    }

    public boolean isNeedRollback() {
        return needRollback;
    }

    public void setNeedRollback(boolean needRollback) {
        if (needTx) {
            this.needRollback = needRollback;
        }
    }

    public MultipartRequest getMultipartRequest() {
        return multipartRequest;
    }

    public ServletContext getApplication() {
        return application;
    }


    public boolean hasNextAction() {
        return nextAction != null;
    }


    public void clearNextAction() {
        nextAction = null;
    }

    public String getNextAction() {
        return nextAction;
    }


    public void setNextAction(String nextAction) {
        this.nextAction = nextAction;
    }


    public String getUrlParam(int index) {
        if (urlParam == null || index > urlParam.length - 1) {
            return null;
        }
        return urlParam[index];
    }

    public Throwable getControllerOrInterceptorException() {
        return controllerOrInterceptorException;
    }


    public void setControllerOrInterceptorException(Throwable controllerOrInterceptorException) {
        this.controllerOrInterceptorException = controllerOrInterceptorException;
    }


    public Throwable getServiceException() {
        return serviceException;
    }


    public void setServiceException(Throwable serviceException) {
        this.serviceException = serviceException;
    }

    /**
     * 设置数据
     *
     * @param key
     * @param value
     */
    public void setData(String key, Object value) {
        if (dataMap == null) {
            dataMap = new HashMap();
        }
        dataMap.put(key, value);
    }

    /**
     * 读取数据
     *
     * @param key
     * @return
     */
    public Object getData(String key) {
        if (dataMap == null) {
            return null;
        }
        return dataMap.get(key);
    }

}
